home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Sample Code / AOCE Sample Code / PowerTalk Access Modules / Sample SMSAM / SampleSMSAM Source / VirtualFile / FileOfFiles.cp < prev    next >
Encoding:
Text File  |  1995-07-28  |  15.0 KB  |  664 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        FileOfFiles.cp
  3.  
  4.     Copyright:    © 1991-1994 by Apple Computer, Inc.
  5.                 All rights reserved.
  6.  
  7.     Part of the AOCE Sample SMSAM Package.  Consult the license
  8.     which came with this software for your specific legal rights.
  9.  
  10. */
  11.  
  12.  
  13.  
  14. #ifndef    __FILEOFFILES__
  15. #include "FileOfFiles.h"
  16. #endif
  17.  
  18. #ifndef __ERRORS__
  19. #include <Errors.h>
  20. #endif
  21.  
  22. #ifndef __FILES__
  23. #include <Files.h>
  24. #endif
  25.  
  26. #ifndef    __DEBUGASSERT__
  27. #include "DebugAssert.h"
  28. #endif
  29.  
  30. #ifndef    __DEBUGGINGGEAR__
  31. #include "DebuggingGear.h"
  32. #endif
  33.  
  34. #ifndef __VIRTUALRAMORDISKFILE__
  35. #include "VirtualRamOrDiskFile.h"
  36. #endif
  37.  
  38. #ifndef    __STRING__
  39. #include "String.h"
  40. #endif
  41.  
  42. /***********************************|****************************************/
  43.  
  44. #pragma segment FileOfFiles
  45.  
  46. /***********************************|****************************************/
  47.  
  48. extern ostream& DumpHex (ostream& s, const void *p, unsigned long size);
  49.  
  50. /***********************************|****************************************/
  51.  
  52. inline long min ( long a, long b )
  53. {
  54.     return ( a < b) ? a : b;
  55. }
  56.  
  57. /***********************************|****************************************/
  58.  
  59. ostream& TFileOfFiles::operator >> ( ostream& s ) const
  60. {
  61.     s << "TFileOfFiles @ " << (void*) this;
  62.     return TVirtualFile::operator >> ( s );
  63. }
  64.  
  65. /***********************************|****************************************/
  66.  
  67. class TCacheCache
  68. {
  69. public:        TCacheCache ( const TFileOfFiles* );
  70.             TCacheCache ( TVirtualFile*, long offset, unsigned long index );
  71.             TCacheCache ();
  72.     virtual    ~TCacheCache ();
  73.  
  74.             void                    Save ( const TFileOfFiles* );
  75.             void                    Restore ( TFileOfFiles* ) const;
  76.  
  77. private:    TVirtualFile*            fCacheFile;
  78.             long                    fCacheOffset;
  79.             unsigned long            fCacheIndex;
  80. };
  81.  
  82. /***********************************|****************************************/
  83.  
  84. TCacheCache::TCacheCache ( const TFileOfFiles* files ):
  85.     fCacheFile ( files->fCacheFile ),
  86.     fCacheOffset ( files->fCacheOffset ),
  87.     fCacheIndex ( files->fCacheIndex )
  88. {
  89. }
  90.  
  91. /***********************************|****************************************/
  92.  
  93. TCacheCache::TCacheCache ( TVirtualFile* file, long offset, unsigned long index ):
  94.     fCacheFile ( file ),
  95.     fCacheOffset ( offset ),
  96.     fCacheIndex ( index )
  97. {
  98. }
  99.  
  100. /***********************************|****************************************/
  101.  
  102. TCacheCache::~TCacheCache ()
  103. {
  104. }
  105.  
  106. /***********************************|****************************************/
  107.  
  108. void
  109. TCacheCache::Save ( const TFileOfFiles* files )
  110. {
  111.     fCacheFile = files->fCacheFile;
  112.     fCacheOffset = files->fCacheOffset;
  113.     fCacheIndex = files->fCacheIndex;
  114. }
  115.  
  116. /***********************************|****************************************/
  117.  
  118. void
  119. TCacheCache::Restore ( TFileOfFiles* files ) const
  120. {
  121.     files->fCacheFile = fCacheFile;
  122.     files->fCacheOffset = fCacheOffset;
  123.     files->fCacheIndex = fCacheIndex;
  124. }
  125.  
  126. /***********************************|****************************************/
  127.  
  128. static unsigned long gTestFileCount = 0;
  129. TVirtualFile* CreateTestFile ()
  130. {
  131.     TVirtualFile* file = new TVirtualRamOrDiskFile;
  132.  
  133.     FAILNULL ( file );
  134.     FAILOSErr ( file->SetEnd ( 0 ) );
  135.     FAILOSErr ( file->SetPosition ( fsFromStart, 0 ) );
  136.  
  137.     unsigned long stop = ++gTestFileCount;
  138.  
  139.     for ( unsigned long i = 1; i <= stop; i++ )
  140.     {
  141.         FAILOSErr ( file->Write ( (const char) ( gTestFileCount + '@' ) ) );
  142.         FAILOSErr ( file->Write ( (const char) ( i + (char) '0' ) ) );
  143.     }
  144.  
  145.     FAILOSErr ( file->Write ( '|' ) );
  146.  
  147.     return file;
  148. }
  149.  
  150. /***********************************|****************************************/
  151.  
  152. TVirtualFile* CreateTestFileOfFiles ()
  153. {
  154.     TFileOfFiles* fileOfFiles = new TFileOfFiles;
  155.     fileOfFiles->AppendFile ( CreateTestFile () );
  156.     fileOfFiles->AppendFile ( CreateTestFile () );
  157.     fileOfFiles->AppendFile ( CreateTestFile () );
  158.     return fileOfFiles;
  159. }
  160.  
  161. /***********************************|****************************************/
  162.  
  163. #if debug
  164.  
  165. void VirtualFileTest ( TVirtualFile* file )
  166. {
  167.     FAILNULL ( file );
  168.  
  169.     chris << "TVirtualFile:" << *file << endl;
  170.  
  171.     FAILOSErr ( file->SetPosition ( fsFromStart, 0 ) );
  172.     chris << "after SetPosition(fsFromStart,0):" << *file << endl;
  173.  
  174.     FAILOSErr ( file->SetPosition ( fsFromLEOF, 0 ) );
  175.     chris << "after SetPosition(fsFromLEOF,0):" << *file << endl;
  176.  
  177.     FAILOSErr ( file->SetEnd ( 10 ) );
  178.     chris << "after SetEnd(10):" << *file << endl;
  179.  
  180.     FAILOSErr ( file->SetEnd ( 20 ) );
  181.     chris << "after SetEnd(20):" << *file << endl;
  182.  
  183.     FAILOSErr ( file->SetEnd ( 0 ) );
  184.     chris << "after SetEnd(0):" << *file << endl;
  185.  
  186.     const char* string = "this is a test string";
  187.     FAILOSErr ( file->Write ( string ) );
  188.     chris << "after Write(string):" << *file << endl;
  189. }
  190.  
  191. /***********************************|****************************************/
  192.  
  193. void FileOfFilesTests ()
  194. {
  195.     TRY
  196.     {
  197.         TFileOfFiles* file = new TFileOfFiles;
  198.  
  199.         file->AppendFile ( CreateTestFileOfFiles () );
  200.         file->AppendFile ( CreateTestFileOfFiles () );
  201.         file->AppendFile ( CreateTestFileOfFiles () );
  202.  
  203.         VirtualFileTest ( file );
  204.  
  205.         for ( unsigned long index = 1; index <= file->NumFiles (); index++ )
  206.             VirtualFileTest ( file->GetIndexFile ( index ) );
  207.  
  208.         VirtualFileTest ( file );
  209.     }
  210.     EXCEPTION
  211.     {
  212.         chris << "\n### WE CAUGHT AN EXCEPTION!!!!" << endl;
  213.     }
  214.     ENDEXCEPTION
  215. }
  216.  
  217. #endif
  218.  
  219. /***********************************|****************************************/
  220.  
  221. TFileOfFiles::TFileOfFiles():
  222.     TVirtualFile (),
  223.     fFileList ( new TVirtualFileList ),
  224.     fPosition ( 0 )
  225. {
  226.     InvalidateCache ();
  227. }
  228.  
  229. //--------------------------------------------------------------------------------
  230.  
  231. TFileOfFiles::~TFileOfFiles()
  232. {
  233.     unsigned long count = NumFiles ();
  234.  
  235.     while ( count > 0 )
  236.         RemoveIndexFile ( count-- );
  237.  
  238.     delete fFileList;
  239. }
  240.  
  241. //--------------------------------------------------------------------------------
  242.  
  243. TVirtualFile* TFileOfFiles::RemoveIndexFile(unsigned long index)
  244. {
  245.     InvalidateCache ();
  246.  
  247.     TVirtualFile* file = fFileList->Remove(index);
  248.  
  249.     if ( file )
  250.         file->UnregisterReference();
  251.  
  252.     return file;
  253. }
  254.  
  255. //--------------------------------------------------------------------------------
  256.  
  257. OSErr TFileOfFiles::AppendFile(TVirtualFile* file)
  258. {
  259.     return InsertFile ( file, NumFiles () + 1 );
  260. }
  261.  
  262. //--------------------------------------------------------------------------------
  263.  
  264. OSErr TFileOfFiles::InsertFile(TVirtualFile* file,unsigned long index)
  265. {
  266.     FAILNULL ( file );
  267.     TVirtualFile* cacheFile = fCacheFile;
  268.     long cacheOffset = fCacheOffset;
  269.     unsigned long cacheIndex = fCacheIndex;
  270.  
  271.     steveF ( 18, "TFileOfFile:InsertFile(), index=" << index );
  272.     
  273.     file->RegisterReference();
  274.  
  275.     if ( index >= 1 && index <= NumFiles () )
  276.     {
  277.         TVirtualFile* prevFile = fFileList->Get ( index );
  278.  
  279.         if ( prevFile )
  280.             prevFile->UnregisterReference();
  281.     }
  282.  
  283.     InvalidateCache ();
  284.     fFileList->Insert ( index, file );
  285.  
  286.     return noErr;
  287. }
  288.  
  289. //--------------------------------------------------------------------------------
  290.  
  291. OSErr TFileOfFiles::GetSubFile ( unsigned long position, TVirtualFile*& subFile, long& offsetInSubfile, unsigned long& subFileIndex, Boolean makeFiles )
  292. {
  293.     unsigned long offsetSoFar = 0; long chunkLength;
  294.     const unsigned long numFiles = NumFiles ();
  295.     OSErr error = noErr; subFileIndex = 0;
  296.  
  297.     // lock ourselves down because this routine might move memory yet it is
  298.     // passed a reference to one of our members which might become invalid
  299.     // if this object moves.
  300.  
  301.     ( (TFileOfFiles*) this )->Lock ();
  302.  
  303.     do
  304.     {
  305.         subFile = ++subFileIndex <= numFiles ? GetIndexFile ( subFileIndex ) : nil;
  306.  
  307.         // if no subfile exists, then we have to make a subfile and add it to the list
  308.         if ( !subFile && makeFiles )
  309.         {
  310.             subFile = new TVirtualRamOrDiskFile ();
  311.             if ( subFile )
  312.             {
  313.                 error = subFile->SetEnd ( position - offsetSoFar );
  314.                 if ( error )
  315.                 {
  316.                     // we failed in setting up the subfile
  317.                     delete subFile;
  318.                     subFile = nil;
  319.                     break;
  320.                 }
  321.                 else
  322.                 {
  323.                     // now let’s add the subfile; save the cache because it might get
  324.                     // invalidated across this call since a reference is being passed into our routine
  325.     
  326.                     TCacheCache cache ( this );
  327.                     ( (TFileOfFiles*) this )->AppendFile ( subFile );
  328.                     (void) cache.Restore ( this );
  329.                 }
  330.             }
  331.         }
  332.  
  333.         if ( subFile )
  334.         {
  335.             error = subFile->GetEnd ( chunkLength );
  336.             if ( error )
  337.                 break;
  338.  
  339.             offsetInSubfile = position - offsetSoFar;
  340.             offsetSoFar += chunkLength;
  341.         }
  342.         else
  343.         {
  344.             error = eofErr;
  345.         }
  346.     }
  347.     while ( subFile && !error && offsetSoFar < position );
  348.  
  349.     ( (TFileOfFiles*) this )->Unlock ();
  350.  
  351.     return error;
  352. }
  353.  
  354. /***********************************|****************************************/
  355.  
  356. OSErr
  357. TFileOfFiles::ValidateCache ( Boolean makeFile )
  358. {
  359.     OSErr error = noErr;
  360.  
  361.     if ( true || !fCacheFile )
  362.     {    
  363.         error = GetSubFile ( fPosition, fCacheFile, fCacheOffset, fCacheIndex, makeFile );        
  364.         if ( error )
  365.             InvalidateCache ();
  366.         else 
  367.         {
  368.             error = fCacheFile->SetPosition ( fsFromStart, fCacheOffset );
  369.             if ( error )
  370.                 InvalidateCache ();
  371.         }
  372.     }
  373.  
  374.     return error;
  375. }
  376.  
  377. /***********************************|****************************************/
  378.  
  379. void TFileOfFiles::InvalidateCache ()
  380. {
  381.     fCacheFile = nil;
  382.     fCacheOffset = 0;
  383.     fCacheIndex = 0;
  384. }
  385.  
  386. /***********************************|****************************************/
  387.  
  388. OSErr
  389. TFileOfFiles::GetNextCacheFile ()
  390. {
  391.     fCacheFile = ++fCacheIndex <= NumFiles () ? GetIndexFile ( fCacheIndex ) : nil;
  392.     return fCacheFile ? fCacheFile->SetPosition ( fsFromStart, fCacheOffset = 0 ) : eofErr;
  393. }
  394.  
  395. /***********************************|****************************************/
  396.  
  397. OSErr TFileOfFiles::ReadData ( void* bufferStart, long& bytesToRead )
  398. {
  399.     char* buffer = (char*) bufferStart;
  400.     long chunkLength = 0, bytesRemaining = bytesToRead;
  401.     OSErr error = ValidateCache ( false ); Boolean cacheUpdate;
  402.  
  403.     #if debug
  404.     if (steveFlag.Flag(18)) {
  405.         keith << "TFileOfFiles::ReadData(" << bytesToRead << ").  fPos=" << fPosition << endl;
  406.     }
  407.     #endif    
  408.  
  409.     while ( fCacheFile && !error && bytesRemaining > 0 )
  410.     {
  411.         long fileLength;
  412.         error = fCacheFile->GetEnd ( fileLength );
  413.         if ( error )
  414.             break;
  415.  
  416.         chunkLength = fileLength - fCacheOffset;
  417.  
  418.         if ( chunkLength >= bytesRemaining )
  419.         {
  420.             chunkLength = bytesRemaining;
  421.             cacheUpdate = false;
  422.         }
  423.         else
  424.             cacheUpdate = true;
  425.  
  426.         #if debug
  427.         if (steveFlag.Flag(18)) {
  428.             keith << "TFileOfFiles::Reading " << chunkLength << " bytes from subfile " << fCacheIndex << 
  429.                         " (of " << NumFiles() << " size=" << fileLength << ")" << endl;
  430.         }
  431.         #endif
  432.         
  433.         error = fCacheFile->ReadData ( buffer, chunkLength );
  434.         if ( error )
  435.         {
  436.             #if debug
  437.             if (steveFlag.Flag(18)) {
  438.                 keith << "TFileOfFiles::Read " << chunkLength << " bytes done. err=" << error << " remaining=" <<
  439.                             bytesRemaining << endl;
  440.             }
  441.             #endif        
  442.     
  443.             break;
  444.         }
  445.  
  446.         #if debug
  447.         if (steveFlag.Flag(18)) {
  448.             keith << "TFileOfFiles::Read " << chunkLength << " bytes done. err=" << error << " remaining=" << 
  449.                         bytesRemaining << endl;
  450.             DumpHex(keith, buffer, min ( chunkLength, 48 ) );
  451.         }
  452.         #endif        
  453.  
  454.         bytesRemaining -= chunkLength;
  455.         buffer += chunkLength;
  456.         fPosition += chunkLength;
  457.  
  458.         if ( bytesRemaining > 0 || cacheUpdate )
  459.             error = GetNextCacheFile ();
  460.     }
  461.  
  462.     bytesToRead -= bytesRemaining;
  463.  
  464.     #if debug
  465.     if (steveFlag.Flag(18)) {
  466.         keith << "TFileOfFiles::ReadData(" << bytesToRead << ") completed, err=" << error << 
  467.                     " fPos=" << fPosition << endl;
  468.     }
  469.     #endif    
  470.  
  471.     return error;
  472. }
  473.  
  474. /***********************************|****************************************/
  475.  
  476. OSErr
  477. TFileOfFiles::Consolidate ()
  478. {
  479.     NOT_IMPLEMENTED ();
  480.     return noErr;
  481. }
  482.  
  483. /***********************************|****************************************/
  484.  
  485. OSErr TFileOfFiles::WriteData ( const void* bufferStart, long& bytesToWrite )
  486. {
  487.     char* buffer = (char*) bufferStart;
  488.     long chunkLength = 0, bytesRemaining = bytesToWrite;
  489.     OSErr error = ValidateCache ( true ); unsigned long numFiles = NumFiles ();
  490.  
  491.     while ( bytesRemaining > 0 && fCacheFile && !error )
  492.     {
  493.         long fileLength = 0;
  494.         if ( fCacheIndex >= numFiles )
  495.         {
  496.             chunkLength = bytesRemaining;
  497.         }
  498.         else
  499.         {
  500.             error = fCacheFile->GetEnd ( fileLength );
  501.             if ( error )
  502.                 break;
  503.  
  504.             chunkLength = fileLength - fCacheOffset;
  505.  
  506.             if ( chunkLength > bytesRemaining )
  507.                 chunkLength = bytesRemaining;
  508.         }
  509.  
  510.         #if debug
  511.         if (steveFlag.Flag(18)) {
  512.             keith << "TFileOfFiles::WriteData(" << chunkLength << ") bytes to subfile " << fCacheIndex << 
  513.                         " (of " << NumFiles() << " size=" << fileLength << ")" << endl;
  514.             DumpHex(keith, buffer, min ( chunkLength, 48 ) );
  515.         }
  516.         #endif        
  517.  
  518.         error = fCacheFile->WriteData ( buffer, chunkLength );
  519.         if ( error )
  520.         {
  521.             #if debug
  522.             if (steveFlag.Flag(18)) {
  523.                 keith << "TFileOfFiles::WriteData(), err=" << error << endl;
  524.             }
  525.             #endif        
  526.             break;
  527.         }
  528.  
  529.         #if debug
  530.         if (steveFlag.Flag(18)) {
  531.             keith << "TFileOfFiles::WriteData(" << chunkLength << ") bytes done. Remaining=" << 
  532.                         bytesRemaining << endl;
  533.         }
  534.         #endif        
  535.  
  536.         bytesRemaining -= chunkLength;
  537.         buffer += chunkLength;
  538.         fPosition += chunkLength;
  539.  
  540.         if ( bytesRemaining > 0 )
  541.             error = GetNextCacheFile ();
  542.     }
  543.  
  544.     bytesToWrite -= bytesRemaining;
  545.  
  546.     return error;
  547. }
  548.  
  549. /***********************************|****************************************/
  550.  
  551. OSErr TFileOfFiles::GetEnd ( long& length ) const
  552. {
  553.     OSErr error = noErr;
  554.     long totalLength = 0, subLength;
  555.     unsigned long count = NumFiles ();
  556.  
  557.     while ( count > 0 )
  558.     {
  559.         const TVirtualFile* file = GetIndexFile ( count-- );
  560.  
  561.         if ( file )
  562.         {
  563.             error = file->GetEnd ( subLength );
  564.             if ( error )
  565.                 break;
  566.             else
  567.                 totalLength += subLength;
  568.         }
  569.         else
  570.         {
  571.             error = -1;
  572.             break;
  573.         }
  574.     }
  575.  
  576.     length = totalLength;
  577.  
  578.     return error;
  579. }
  580.  
  581. /***********************************|****************************************/
  582.  
  583. unsigned long
  584. TFileOfFiles::GetEnd () const
  585. {
  586.     long end = 0;
  587.     FAILOSErr ( GetEnd ( end ) );
  588.     return end;
  589. }
  590.  
  591. /***********************************|****************************************/
  592.  
  593. OSErr MoveEnd ( TVirtualFile& file, long delta )
  594. {
  595.     long end = 0;
  596.     OSErr error = file.GetEnd ( end );
  597.     if ( !error ) error = file.SetEnd ( end + delta );
  598.     return error;
  599. }
  600.  
  601. /***********************************|****************************************/
  602.  
  603. OSErr TFileOfFiles::SetEnd ( long requestedLength )
  604. {
  605.     InvalidateCache ();
  606.  
  607.     OSErr error = noErr;
  608.     TVirtualFile* subFile = nil;
  609.     unsigned long count = NumFiles ();
  610.     long actualLength = GetEnd ();
  611.  
  612.     if ( requestedLength < actualLength )
  613.     {
  614.         while ( count > 1 ) RemoveIndexFile ( count-- );
  615.         subFile = GetIndexFile ( 1 ); FAILNULL ( subFile );
  616.         error = subFile->SetEnd ( requestedLength );
  617.     }
  618.     else if ( requestedLength > actualLength )
  619.     {
  620.         if ( count == 0 ) InsertFile ( subFile = new TVirtualRamOrDiskFile, count++ );
  621.         subFile = GetIndexFile ( count ); FAILNULL ( subFile );
  622.         error = MoveEnd ( *subFile, requestedLength - actualLength );
  623.     }
  624.  
  625.     if ( fPosition > requestedLength )
  626.         fPosition = requestedLength;
  627.  
  628.     return error;
  629. }
  630.  
  631. /***********************************|****************************************/
  632.  
  633. OSErr TFileOfFiles::SetPosition ( short mode, long offset )
  634. {
  635.     InvalidateCache ();
  636.  
  637.     long end = GetEnd ();
  638.  
  639.     if ( mode == fsFromLEOF )
  640.         offset += end;
  641.     else if ( mode == fsFromMark )
  642.         offset += fPosition;
  643.  
  644.     if ( offset > end )
  645.     {
  646.         return eofErr;
  647.     }
  648.     else
  649.     {
  650.         fPosition = offset;
  651.         return noErr;
  652.     }
  653. }
  654.  
  655. /***********************************|****************************************/
  656.  
  657. OSErr TFileOfFiles::GetPosition ( long& position ) const
  658. {
  659.     position = fPosition;
  660.     return noErr;
  661. }
  662.  
  663. /***********************************|****************************************/
  664.